// rpmt.go
//usage:
// compile so: go build -o librpmt.so -buildmode=c-shared rpmt.go
// ldd /usr/local/nginx/sbin/nginx
// mv librpmt.so /lib/x86_64-linux-gnu/
package main // 代码包声明语句。

import "C"

import (
	"bytes"
	"container/list"
	"encoding/binary"
	"fmt"
	"io/ioutil"
	"strconv"
)

//export GetOnlyAudioTsPackage
//因为不知道如何进行参数的传送，所以采用传时间戳的方式来进行文件的处理
func GetOnlyAudioTsPackage(tm int) {
	l := ReadTSFileToList(tm)
	pid := GetPAT_PID(l)
	l1 := GetOnlyAudioList(l, pid)
	SaveListToFile(tm, l1)
}

//每个TS-Package的长度为188，所以把输入的二进制内容分解成188个字节的内容
func SplitTSPackage(bdata []byte) []byte {
	return []byte("")
}
func BytesCombine(pBytes ...[]byte) []byte {
	return bytes.Join(pBytes, []byte(""))
}

//通过pid来获取PMT,并对于PMT中的内容进行更写，把除audio以外的的节目都屏蔽掉
func GetOnlyAudioList(l list.List, pid uint16) list.List {

	var npmt []byte
	for e := l.Front(); e != nil; e = e.Next() {
		h3 := e.Value.([]byte)[0:3]
		if h3[0] != 0x47 {
			continue
		}
		h1 := h3[1] << 3 >> 3
		vpid := []byte{h1, h3[2]}
		npid := uint16(binary.BigEndian.Uint16(vpid))
		if npid == pid {
			//使用新的pmt内容进行覆盖原内容
			if npmt == nil {
				npmt = GetNewPMT(e.Value.([]byte))
			}
			e.Value = npmt
			continue
		}
	}
	return l
}

//改写pmt的内容，把除音频外的内容屏蔽掉
func GetNewPMT(e []byte) []byte {
	//PMT表的表名为02,包的第6个位的值代表表名，一定是02；
	if e[5] != 0x02 {
		//如果不为pmt包，则不更改内容返回;
		return e
	}
	//获取PMT的长度信息
	pmt_len := e[7]
	pmt_len1 := pmt_len - 13
	//一般来说PMT的第17字节开始，是流的类型描述；先找到音频的描述elementary_PID，然后把其它的流进行屏蔽
	//因为每个流后面还有可能存在描述信息，所以需要把第五位的长度先获取，如果长度为0，则只取前5位即可；
	i := 0
	var pmt_audio []byte
	for {
		var bp []byte
		bp = e[i+22 : i+27]
		m := (int)(bp[4])
		if m > 0 {
			bp = e[i+22 : i+27+m]
		}
		i = i + 5 + m
		//找到PMT中的音频频道
		if bp[0] == 0x0f {
			pmt_audio = bp
			break
		}
	}
	if pmt_audio == nil {
		return e
	}
	//如果找到PMT中的音频频道，则进行音频的处理，如果有多条音频輑道，可能会出问题（只会处理一条轨，以后再看看如何优化)
	slices := make([]byte, pmt_len1)
	copy(slices, e[0:pmt_len1])
	copy(slices[0:4], pmt_audio[0:4])
	slices[4] = pmt_len1 - 5
	//提取前5-17位，与音频进行相加，然后做crc运算
	epmt := append(e[5:17], slices...)
	bcrc := make([]byte, 4)
	// for _, b := range epmt[:len(epmt)] {

	// 	s := strconv.FormatInt(int64(b&0xff), 16)
	// 	print(s + " ")
	// }
	// binary.BigEndian.PutUint32(bcrc, crc32.ChecksumIEEE(epmt))
	binary.BigEndian.PutUint32(bcrc, CalCrcMpeg2(epmt))
	// for _, b := range bcrc[:len(bcrc)] {

	// 	s := strconv.FormatInt(int64(b&0xff), 16)
	// 	print(s + " ")
	// }
	n_e := append((append(e[0:5], epmt...)), bcrc...)
	n_e1 := append(n_e, e[len(n_e):]...)
	return n_e1
}

//通过list来获取PAT中的PID值
func GetPAT_PID(l list.List) uint16 {

	for e := l.Front(); e != nil; e = e.Next() {
		h3 := e.Value.([]byte)[:3]
		if h3[0] != 0x47 {
			continue
		}
		//如果后12位为0x000则该package为PAT
		h1 := h3[1] << 3 >> 3
		if h1 != 0 || h3[2] != 0 {
			continue
		}
		//正常情况需要解析PAT的数据内容，但为了更方便直接使用粗暴的方式进行处理，默认总体的数据长度是13字节，PID是后4-5位，可能有些场景会出错；
		hm := e.Value.([]byte)[15:17]
		m1 := (uint8)(hm[0] << 3 >> 3)
		m2 := (uint8)(hm[1])

		var m uint16
		m = ((m | (uint16)(m1)) << 8) | (uint16)(m2)
		return (m)
	}
	return (uint16)(9999)
}

func ReadTSFileToList(tm int) list.List {

	//从/tmp/vdisk_aac/"+tm+".tmp"
	tmpfile := "/tmp/vdisk_aac/" + strconv.Itoa(tm) + ".tmp"
	l := list.New()
	b, err := ioutil.ReadFile(tmpfile) // just pass the file name
	if err != nil {
		fmt.Print(err)
	}
	//对于每个ts的package都是188个，所以需要把二进制串都分解成每个为188的字节串
	//print(len(b))
	fnum := (len(b) + 187) / 188
	for i := 0; i < fnum; i++ {
		line := b[i*188 : (i+1)*188]
		l.PushBack(line)
	}
	return *l
}
func ReadTSDataToList(tsdata []byte) list.List {

	l := list.New()
	fnum := (len(tsdata) + 187) / 188
	for i := 0; i < fnum; i++ {
		line := tsdata[i*188 : (i+1)*188]
		l.PushBack(line)
	}
	return *l
}
func SaveListToFile(tm int, l list.List) {

	tmpfile := "/tmp/vdisk_aac/" + strconv.Itoa(tm) + ".tmp.1"
	var r []byte
	for e := l.Front(); e != nil; e = e.Next() {
		r = append(r, e.Value.([]byte)...)
	}
	ioutil.WriteFile(tmpfile, r, 0644)
}

func CalCrcMpeg2(msg []byte) uint32 {

	var i, j int
	var crc, msb uint32

	crc = 0xFFFFFFFF
	for i = 0; i < len(msg); i++ {
		// xor next byte to upper bits of crc
		crc ^= ((uint32)(msg[i]) << 24)
		for j = 0; j < 8; j++ { // Do eight times.
			msb = crc >> 31
			crc <<= 1
			crc ^= (0 - msb) & 0x04C11DB7
		}
	}
	return crc // don't complement crc on output
}

func main() {
	GetOnlyAudioTsPackage(1628508327419)
}